package com.atguigu.exam.service.impl;


import com.atguigu.exam.entity.*;
import com.atguigu.exam.mapper.*;
import com.atguigu.exam.service.PaperService;
import com.atguigu.exam.vo.AiPaperVo;
import com.atguigu.exam.vo.PaperVo;
import com.atguigu.exam.vo.RuleVo;
import com.baomidou.mybatisplus.core.conditions.query.LambdaQueryWrapper;
import com.baomidou.mybatisplus.core.conditions.query.QueryWrapper;
import com.baomidou.mybatisplus.extension.service.impl.ServiceImpl;
import lombok.extern.slf4j.Slf4j;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Service;
import org.springframework.transaction.annotation.Transactional;

import java.math.BigDecimal;
import java.sql.Array;
import java.util.ArrayList;
import java.util.Collections;
import java.util.List;
import java.util.Map;
import java.util.stream.Collectors;


/**
 * 试卷服务实现类
 */
@Slf4j
@Service
public class PaperServiceImpl extends ServiceImpl<PaperMapper, Paper> implements PaperService {

    @Autowired
    private PaperQuestionMapper paperQuestionMapper;

    @Autowired
    private QuestionMapper questionMapper;

    @Autowired
    private QuestionChoiceMapper questionChoiceMapper;

    @Autowired
    private QuestionAnswerMapper questionAnswerMapper;

    /**
     * 获取带题目的试卷详情
     * @param paperId
     * @return
     */
    @Override
    public Paper getPaperWithQuestions(Integer paperId) {
//        1.查询试卷基本信息
        Paper paper = baseMapper.selectById(paperId);
//        查询试卷
        if (paper == null){
            return null; //如果试卷不存在，返回null
        }
//        2.查询试卷包含的所有题目id和分值
        LambdaQueryWrapper<PaperQuestion> pqWrapper = new LambdaQueryWrapper<>();
        pqWrapper.eq(PaperQuestion::getPaperId,paperId); //设置查询条件
        List<PaperQuestion> paperQuestions = paperQuestionMapper.selectList(pqWrapper);
        if (paperQuestions.isEmpty()){
            paper.setQuestions(new ArrayList<>());
            return paper;
        }
//        3.给题目设置真实分数！题目的分数是默认分数，真实分数存储在pageQuestion表中！
        List<Long> questionIds = paperQuestions.stream().map(PaperQuestion::getQuestionId).collect(Collectors.toList());
//        根据题目id查询题目详情：
        List<Question> questions = questionMapper.selectBatchIds(questionIds);
//        4.优化：批量查询避免N+1问题
//        填充分值信息：
        Map<Long, BigDecimal> scoreMap = paperQuestions.stream().collect(Collectors.toMap(PaperQuestion::getQuestionId, PaperQuestion::getScore));
//       批量查询选项:
        List<QuestionChoice> allChoices = questionChoiceMapper.selectList(new QueryWrapper<QuestionChoice>().in("question_id", questionIds));
        Map<Long, List<QuestionChoice>> choicesMap = allChoices.stream().collect(Collectors.groupingBy(QuestionChoice::getQuestionId));
//        批量查询答案：
        List<QuestionAnswer> allAnswers = questionAnswerMapper.selectList(new QueryWrapper<QuestionAnswer>().in("question_id", questionIds));
        Map<Long, QuestionAnswer> answersMap = allAnswers.stream().collect(Collectors.toMap(QuestionAnswer::getQuestionId, answer -> answer));

//        5.为每个题目设置信息：
        questions.forEach(q->{
//            设置分值：
            q.setPaperScore(scoreMap.get(q.getId()));
//            设置选项(仅选择题)：
            if ("CHOICE".equals(q.getType())){
                List<QuestionChoice> choices = choicesMap.getOrDefault(q.getId(), new ArrayList<>());
//                按sort字段排序：
                choices.sort((c1,c2)->Integer.compare(
                        c1.getSort() != null ? c1.getSort() : 0,
                        c2.getSort() != null ? c2.getSort() : 0
                ));
                q.setChoices(choices);
            }
//            设置答案
            q.setAnswer(answersMap.get(q.getId()));
        });
//        5.按题目类型排序：选择题 -> 判断题 -> 简答题
        questions.sort((q1,q2)->{
            int order1 = getTypeOrder(q1.getType());
            int order2 = getTypeOrder(q2.getType());
            return Integer.compare(order1,order2);
        });
        paper.setQuestions(questions); //设置题目列表
        return paper;
    }


    /**
     * 创建试卷：手动组卷
     * @param paperVo
     * @return
     */
    @Override
    public Paper createPaper(PaperVo paperVo) {
//        1、创建试卷对象
        Paper paper = new Paper();
        paper.setName(paperVo.getName());
        paper.setDescription(paperVo.getDescription());
        paper.setDuration(paperVo.getDuration());
        paper.setStatus("DRAFT"); //新建的试卷默认为草稿状态

//        2.计算总分和题目数量:BigDecimal.ZERO：初始值（0），作为累加的起点。
//BigDecimal::add：累加器，是 BigDecimal 类的 add 方法引用，用于将两个分数相加。
        BigDecimal totalScore = paperVo.getQuestions().values().stream().reduce(BigDecimal.ZERO, BigDecimal::add);
        paper.setQuestionCount(paperVo.getQuestions().size()); //设置题目数量
        baseMapper.insert(paper); //插入试卷记录到数据库
        List<PaperQuestion> paperQuestions = paperVo.getQuestions().entrySet().stream()
                .map(entry -> new PaperQuestion(Math.toIntExact(paper.getId()),entry.getKey().longValue(),entry.getValue())).toList();

        paperQuestionMapper.insertBatchSomeColumn(paperQuestions);
        return paper;
    }

    /**
     * 智能创建试卷（ai组卷）
     * @param aiPaperVo
     * @return
     */
    @Override
    public Paper createPaperWithAi(AiPaperVo aiPaperVo) {
//        1.创建并保存试卷基本信息
        Paper paper = new Paper();
        paper.setName(aiPaperVo.getName());
        paper.setDescription(aiPaperVo.getDescription());
        paper.setDuration(aiPaperVo.getDuration());
        paper.setStatus("DRAFT"); //初始状态
        baseMapper.insert(paper);

//        2.根据规则抽取题目
        int totalQuestionCount = 0;
//        总分数，不能直接根据规则计算：
        BigDecimal totalScore = BigDecimal.ZERO;
        for (RuleVo rule : aiPaperVo.getRules()) {
//            单一规则处理（题目查询，真实题数，真实分数）
//            规则题目数量为0时停止：
            if (rule.getCount() == null || rule.getCount() == 0){
                log.info("规则{}的题目数量为0，已跳过",rule);
                continue;
            }

//            构建查询条件，进行问题查询
            LambdaQueryWrapper<Question> queryWrapper = new LambdaQueryWrapper<>();
//            题目类型 choice、judge、text
            queryWrapper.eq(Question::getType,rule.getType().name());
//            如果指定了题目分类，则加入分类查询条件：
            if (rule.getCategoryIds() != null && !rule.getCategoryIds().isEmpty()){
                queryWrapper.in(Question::getCategoryId,rule.getCategoryIds());
            }else {
//                如果前端没传分类id，我们应该查询该大类下的所有题目，这需要一个逻辑来找到大类下的所有子分类id
                log.warn("规则{}没有提供具体的分类id，将在此类型下所有题目中随机选择",rule.getType());
            }
//            为了避免limit数量大于实际数量的问题，我们先查询id，再随机选取
            queryWrapper.select(Question::getId); //这里只查询id，再随机选取
//            可以进行组卷的题目id集合：
            List<Long> availabelQuestionIds = questionMapper.selectList(queryWrapper)
                    .stream()
                    .map(Question::getId)
                    .collect(Collectors.toList());

            if (availabelQuestionIds.isEmpty()){
                log.warn("类型{}下没有找到任何题目，跳过此规则",rule.getType());
                continue;
            }
//            打乱顺序（随机状态）
            Collections.shuffle(availabelQuestionIds);
//            取需要的数量，或者全部（如果不够的话）
            int questionToTake = Math.min(rule.getCount(), availabelQuestionIds.size());

//          根据最小的题目数量进行分数和题目数量计算：
            totalQuestionCount += questionToTake;
            totalScore = totalScore.add(BigDecimal.valueOf(questionToTake * rule.getScore()));
            log.info("规则{}下真实符合题目数量:{},现阶段总题目数{}和总分数{}",rule,questionToTake,totalQuestionCount,totalScore);

//            解决id集合，根据选中的id列表获取完整的题目信息：
//            通过 subList(0, questionToTake)，就是从符合条件的题目 ID 列表中，选取前 questionToTake 个题目 ID
            List<Long> selectedQuestionIds = availabelQuestionIds.subList(0, questionToTake);

            if (selectedQuestionIds.isEmpty()){
                continue;
            }
            List<Question> questions = questionMapper.selectBatchIds(selectedQuestionIds);
            List<PaperQuestion> paperQuestions = new ArrayList<>(questions.size());

//            3.组装并插入试卷-题目的关联关系
            for (Question question : questions) {
                PaperQuestion pq = new PaperQuestion();
                pq.setPaperId((Math.toIntExact(paper.getId())));
                pq.setQuestionId(question.getId());
                pq.setScore(BigDecimal.valueOf(rule.getScore())); //修正类型
//                逐条插入进list：
                paperQuestions.add(pq);
            }
//            批量修改，进行批量插入：
            paperQuestionMapper.insertBatchSomeColumn(paperQuestions);
        }
        paper.setQuestionCount(totalQuestionCount);
        paper.setTotalScore(totalScore);
        baseMapper.updateById(paper);
        return paper;
    }

    @Transactional
    @Override
    public Paper updatePaper(Integer paperId, PaperVo paperVo) {
//        1.更新试卷基本信息
        Paper paper = new Paper();
        paper.setId(Long.valueOf(paperId));
        paper.setName(paperVo.getName());
        paper.setDescription(paperVo.getDescription());
        paper.setDuration(paperVo.getDuration());

//        2.重新计算总分和题目数:
//     paperVo.getQuestions()
//    从前端传递的paperVo对象中获取题目信息，这里返回的应该是一个Map集合
//   （键是题目 ID，值是该题在试卷中的分数，类型为BigDecimal）。
//   BigDecimal.ZERO 是初始值（从 0 开始累加）
//   BigDecimal::add 是累加器，将流中的每个分数依次相加
        BigDecimal totalScore = paperVo.getQuestions().values().stream().reduce(BigDecimal.ZERO, BigDecimal::add);
        paper.setTotalScore(totalScore);
        paper.setQuestionCount(paperVo.getQuestions().size());
        baseMapper.updateById(paper);

//        3.删除旧的试卷-题目关联
        paperQuestionMapper.delete(new LambdaQueryWrapper<PaperQuestion>().eq(PaperQuestion::getPaperId, paperId));
//    这段代码中，paperVo.getQuestions() 返回一个 Map 集合（键是题目 ID，值是该题在试卷中的分数）
//    .entrySet() 方法获取 Map 中所有的键值对（Entry 对象），每个 Entry 包含一个题目 ID 和对应的分数
//    .map()对流中每个键值对Entry进行转换：
//        通过 new PaperQuestion(...) 创建关联关系对象，传入三个参数：
//        试卷 ID（Long.valueOf(paperId)）
//        题目 ID（entry.getKey().longValue()）
//        题目分数（entry.getValue()）

        List<PaperQuestion> paperQuestions = paperVo.getQuestions().entrySet().stream().map(entry -> new PaperQuestion(paperId, entry.getKey().longValue(), entry.getValue())).collect(Collectors.toList());
//        4.批量插入优化
        paperQuestionMapper.insertBatchSomeColumn(paperQuestions);
        return paper;
    }

    /**
     * 获取题目类型的排序顺序
     */
    private int getTypeOrder(String type){
        switch (type){
            case "CHOICE": return 1; //选择题
            case "JUDGE": return 2; //判断题
            case "TEXT": return 3; //简答题
            default: return 4; //其他类型题
        }
    }
}